#ifndef MESH_HPP
#define MESH_HPP

#include "meshBase.hpp"
#include "edges.hpp"
#include "edge.hpp"
#include "cells.hpp"
#include "cell.hpp"

namespace hms
{

template<>
class MeshTraits<Mesh>
{
public:
	using VertexType = Matrix2Xs::ConstColXpr;
	using EdgeType   = Edge;
	using EdgesType  = Edges;
	using CellType   = Cell;
	using CellsType  = Cells;
};

class Mesh : public MeshBase<Mesh>
{
public:
	using Traits = MeshTraits<Mesh>;

private:
	Matrix2Xs  m_vertices;
	// Cells m_cells;

public:
	Mesh() = default;

	/* a move assignment operator is defined, so these constructors must be
	 * explicitly defined or defaulted, otherwise they are implicitly deleted */
	// Mesh( Mesh&& ) noexcept;

	// Mesh( Mesh&  ) = delete;
	// Mesh( const Mesh& ) = delete;

#ifndef NDEBUG
/* these ctors are only for testing purposes */
	Mesh( Matrix2Xs );
	Mesh( Matrix2Xs, Array2Xi, bool );
#endif

	Mesh(
		Matrix2Xs&& vertexCoords,
		Array2Xi&& edgeVertexIndices
	);
	
	Mesh(
		Matrix2Xs&& vertexCoords,
		ArrayX<char>&& nDomainCellEdges,
		const Array4Xi& domainCellVertices
	);

	Mesh(
		Matrix2Xs&& vertexCoords,
		Array2Xi&& edgeVertexIndices,
		ArrayX<char>&& nDomainCellEdges,
		Array4Xi&& domainCellEdges,
		const Ref<const Array4Xi>& domainCellVertices = Array4Xi{}
	);
	
	Mesh(
		Matrix2Xs&& vertexCoords,
		Index nInnerEdges,
		Array2Xi&& edgeVertexIndices,
		Array2Xi&& adjacentCells,
		ArrayX<char>&& nDomainCellEdges,
		Array4Xi&& domainCellEdges,
		const Ref<const Array4Xi>& domainCellVertices = Array4Xi{}
	);
	
	auto vertices() const -> const Matrix2Xs&;
	
private:
	auto vertexImp(Index) const -> Matrix2Xs::ConstColXpr;
	auto edgeImp  (Index) const -> Edge;
	auto cellImp  (Index) const -> Cell;
	
	auto nVerticesImp     () const -> Index;
	auto nEdgesImp        () const -> Index;
	auto nInnerEdgesImp   () const -> Index;
	auto nBoundaryEdgesImp() const -> Index;
	auto nCellsImp        () const -> Index;
	auto nDomainCellsImp  () const -> Index;
	auto nGhostCellsImp   () const -> Index;
	
	auto domainAreaImp    () const -> scalar;

	friend MeshBase;

private:
	void checkBoundaryPatch( const ArrayXi& ) const ;
};

}

#endif